// NOTE:
// Our Game is based on WebGL with the Three.js Extension.

var renderer, scene, camera, pointLight, spotLight;
var fieldWidth = 16; // Spielmodus 1,2,3
var fieldHeight = 16*2; //
var cubeSize = 15;
var fieldMaxWidth = fieldWidth*cubeSize;
var fieldMaxHeight = fieldHeight*cubeSize;
var gameOver = 0;
var playerLeftID = true;
var playerRightID = !playerLeftID;
var playerLeftRows, playerRightRows = 0;
var	mainWidth = window.innerWidth;
var	mainHeight = window.innerHeight;
var dropInterval = 1000;
var gameStarted = false;	// True, if game started via Menu
var pressedPause = false;
var _playMusic = true;
var _playSounds = true;

var field = setMatrixCells(null,fieldHeight,fieldWidth,!playerLeftID,true);
field = setMatrixCells(field,fieldHeight/2,fieldWidth,playerLeftID,false);
var playerLeft = resetPlayerPos(playerLeftID,true);
var playerRight = resetPlayerPos(playerRightID,true);
var cubes;
var running;

function setup() {
	document.addEventListener( 'mousemove', onDocumentMouseMove, false );
	createScene();
	showMenu();			// Load Menu 
	draw();
	playMusic();		// Play "Background"-Music
}

function randomBrick() {
	return bricks[Math.round(Math.random()*(bricks.length-1))];
}

function addPlayerBricks(tempField_,playerID,actBrick,tempXY) {
	var c = 0;
	for (var k=0;k<actBrick[1][0];k++) {
		for (var m=0;m<actBrick[1][1];m++) {
			if  (actBrick[2][c]==1) tempField_[tempXY[0]+k][tempXY[1]+m] = playerID;
			c++;
		}
	}
}

function resetPlayerPos(player,init) {
	var tempBrick = randomBrick();
	var tempXY = (player?[0,Math.round((fieldWidth-tempBrick[1][0])/2)]:[fieldHeight-tempBrick[1][0],Math.round((fieldWidth-tempBrick[1][0]-tempBrick[0][1])/2)]);
	var tempPlayerfield = setMatrixCells(null,fieldHeight,fieldWidth,false,true);
	addPlayerBricks(tempPlayerfield,true,tempBrick,tempXY);
	if (!init) if (checkOverlap(tempPlayerfield,player)) { fieldAdd(tempPlayerfield,player); gameOver = (player?1:2); };
	return [tempBrick,player,0,tempPlayerfield,tempXY]; // Current brick, playerID, lastButtonPressTime, temporary playerfield, temporary playerCoord
}

function createScene() {
	// set some camera attributes
	var VIEW_ANGLE = 25,
	ASPECT = mainWidth / mainHeight,
	NEAR = 10,
	FAR = 10000;

	// create a WebGL renderer, camera and a scene
	renderer = new THREE.WebGLRenderer();
	camera =
		new THREE.PerspectiveCamera(
			VIEW_ANGLE,
			ASPECT,
			NEAR,
			FAR);

	scene = new THREE.Scene();
	scene.add(camera);

	// set a default position for the camera
	camera.position.z = 800;
	camera.position.y = -500;
	camera.lookAt(new THREE.Vector3(0,0,-1));
	camera.updateProjectionMatrix();

	// start the renderer
	renderer.setSize(mainWidth, mainHeight);

	// attach the render-supplied DOM element
	document.getElementById("gameCanvas").appendChild(renderer.domElement);

	addCubes();

	// adding a ground plane
	var ground = new THREE.Mesh(
	new THREE.BoxGeometry(2000,1000,3,1,1,1),
	new THREE.MeshLambertMaterial({ color : 0x888888 }));
	ground.position.z = -132;
	ground.receiveShadow = true;
	scene.add(ground);


	// create a point light
	pointLight = new THREE.PointLight(0xF8D898);

	// set its position
	pointLight.position.x = -1000;
	pointLight.position.y = 0;
	pointLight.position.z = 1000;
	pointLight.intensity = 2.9;
	pointLight.distance = 10000;
	// add to the scene
	scene.add(pointLight);

	// add a spot light
	spotLight = new THREE.SpotLight(0xF8D898);
	spotLight.position.set(0, 0, 460);
	spotLight.intensity = 1.5;
	spotLight.castShadow = true;
	scene.add(spotLight);

	renderer.shadowMapEnabled = true;

	window.addEventListener('resize', onWindowResize, false);
}

function addCubes() {
	field = setMatrixCells(null,fieldHeight,fieldWidth,!playerLeftID,true);
	field = setMatrixCells(field,fieldHeight/2,fieldWidth,playerLeftID,false);
	playerLeft = resetPlayerPos(playerLeftID,false);
	playerRight = resetPlayerPos(playerRightID,false);
	cubes = setMatrixCells(null,fieldHeight,fieldWidth,null,true);
	
	for (var i=0;i<fieldHeight;i++) {
		for (var j=0;j<fieldWidth;j++) {
			tempColor = (playerLeft[3][i][j]||playerRight[3][i][j])?!field[i][j]:field[i][j];
			cubes[i][j] = new THREE.Mesh(
					new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize),
					new THREE.MeshLambertMaterial({
						color : (tempColor?0x111111:0xeeeeeee)
					}));

			cubes[i][j].traverse(function (child) {
				if (child instanceof THREE.Mesh) {
					child.material.map = THREE.ImageUtils.loadTexture('Textures/grid.jpg');
					child.material.needsUpdate = true;
				}
			});

			scene.add(cubes[i][j]);

			cubes[i][j].position.x = (fieldHeight*cubeSize/2*(-1))+(i/fieldHeight*fieldHeight*cubeSize);
			cubes[i][j].position.y = (fieldWidth*cubeSize/2*(-1))+(j/fieldWidth*fieldWidth*cubeSize);
			cubes[i][j].position.z = 5;
			cubes[i][j].receiveShadow = true;
			cubes[i][j].castShadow = true;
		}
	}
}

function removeCubes() {
	for (var i=0;i<fieldHeight;i++) for (var j=0;j<fieldWidth;j++) scene.remove(cubes[i][j]);
}

function setMatrixCells(matrix, height, width, value, init) {
    if (init) matrix = [];
	for (var i = 0; i < height; ++i) {
		if (init) matrix[i] = [];
        for (var j = 0; j < width; ++j) {
            matrix[i][j] = value;
        }
    }
	return matrix;
}

function onWindowResize() {
	mainWidth = window.innerWidth;
	mainHeight = window.innerHeight
	camera.aspect = mainWidth / mainHeight;
	camera.updateProjectionMatrix();
	renderer.setSize(mainWidth, mainHeight);
}

function draw() {
	if (!pressedPause) { if (guiContainer.style.display == 'none') if (Key.isDown(Key.P)) pause(); }
	else if (!Key.isDown(Key.P)) pressedPause = false;;
	
	playersMovement(false);
	miscAnimations();

	// loop draw function call
	setTimeout( function() { requestAnimationFrame( draw ); }, 1000 / 60 );
	
	// draw THREE.JS scene
	renderer.render(scene, camera);	
}

function miscAnimations() {
	var direction;
	var min, max;
	for (var i=0;i<fieldHeight;i++) for (var j=0;j<fieldWidth;j++) {
		min = 2;
		max = 2;
		if (playerLeft[3][i][j] || playerRight[3][i][j]) {
			min = 3.8;
			max = 4.2;
		}
		
		tempColor = (playerLeft[3][i][j]||playerRight[3][i][j])?!field[i][j]:field[i][j];
		//cubes[i][j].material.color.setHex(tempColor?0x111111:0xeeeeeee);
		
		cubes[i][j].traverse(function (child) {
			if (child instanceof THREE.Mesh) {
				child.material.color.setHex(tempColor?0x111111:0xeeeeeee);
				//child.material.map = THREE.ImageUtils.loadTexture('Textures/grid.jpg');
				//child.material.needsUpdate = true;
			}
		});
		
		if (cubes[i][j].scale.z > max) {
			direction = -1;
		} else if (cubes[i][j].scale.z < min) {
			direction = 1;
		} else direction = (0.5-Math.random());
		
		if (cubes[i][j].scale.z != (min+max/2)) cubes[i][j].scale.z += direction * 0.05;
	}
}

function pause() {
	pressedPause = true;
	if (!gameStarted) {
		running = setTimeout(function(){playersMovement(true)},dropInterval);
	} else {
		clearTimeout(running);
	}
	gameStarted = !gameStarted;
}

function playersMovement(tick) {
	var time = Date.now();
	var delayDrop = 250;
	
	if (Key.isDown(Key.ESC)) resetGame()
	
	if (!gameStarted) return;

	fieldAdd(playerLeft[3],playerLeftID,true);
	fieldAdd(playerRight[3],playerRightID,true);
	
	if (tick) {
		running = setTimeout(function(){playersMovement(true)},dropInterval);
		if (gameOver==0) {
			var oldPlayerLeft = copyField(playerLeft[3]);
			moveArray(playerLeft,'dip');
			if (arrIdentical(oldPlayerLeft,playerLeft[3])) {
				fieldAdd(oldPlayerLeft,playerLeftID);
				playerLeft = resetPlayerPos(playerLeftID,false);
			}
			
			var oldPlayerRight = copyField(playerRight[3]);
			moveArray(playerRight,'dip');
			if (arrIdentical(oldPlayerRight,playerRight[3])) {
				fieldAdd(oldPlayerRight,playerRightID);
				playerRight = resetPlayerPos(playerRightID,false);
			}
		} else {
			clearTimeout(running);
			playSndGameOver();		// Play "Game Over"-Sound
			gameStarted = false;	// Set gameStartet to false to stop bricks
			showGameOver();			// Show "Game Over"-Message
		}
	}
	
	if (playerLeft[2] == 0 || (Date.now()-playerLeft[2]) > delayDrop) {
		if (Key.isDown(Key.D) && (playerLeft[2] == 0 || (Date.now()-playerLeft[2]) > delayDrop)) {
			moveArray(playerLeft,'dip');
			if (playerLeft[2] == 0) playerLeft[2] = time;
		}
		if (Key.isDown(Key.W)) {
			moveArray(playerLeft,'up');
			if (playerLeft[2] == 0) playerLeft[2] = time;
		} else if (Key.isDown(Key.S)) {
			moveArray(playerLeft,'down');
			if (playerLeft[2] == 0) playerLeft[2] = time;
		}
		if (Key.isDown(Key.A)) {
			moveArray(playerLeft,'turn');
			if (playerLeft[2] == 0) playerLeft[2] = time;
		}
	}
	
	if (playerRight[2] == 0 || (Date.now()-playerRight[2]) > delayDrop) {
		if (Key.isDown(Key.LEFT)) {
			moveArray(playerRight,'dip');
			if (playerRight[2] == 0) playerRight[2] = time;
		}
		if (Key.isDown(Key.UP)) {
			moveArray(playerRight,'up');
			if (playerRight[2] == 0) playerRight[2]=time;
		} else if (Key.isDown(Key.DOWN)) {
			moveArray(playerRight,'down');
			if (playerRight[2] == 0) playerRight[2]=time;
		}
		if (Key.isDown(Key.RIGHT)) {
			moveArray(playerRight,'turn');
			if (playerRight[2] == 0) playerRight[2]=time;
		}
	}
	
	if (!(Key.isDown(Key.D)||Key.isDown(Key.W)||Key.isDown(Key.S)||Key.isDown(Key.A))) playerLeft[2] = 0;
	if (!(Key.isDown(Key.UP)||Key.isDown(Key.DOWN)||Key.isDown(Key.LEFT)||Key.isDown(Key.RIGHT))) playerRight[2] = 0;
}

function moveArray(mArray,go) {
	// Can't move bricks, if game not started!
	if (!gameStarted) return;

	var tempArray = copyField(mArray[3]);
	var tempCount = arrCheckSum(tempArray);
	var startI = mArray[4][0];
	var startJ = mArray[4][1];
			
	try {
		if (go == 'dip') {
			if (mArray[1]) {
				for (var i=fieldHeight-1;i>=0;i--) {
					for (var j=0;j<fieldWidth;j++) {
						if (i <= 0) mArray[3][i][j] = false;
						else mArray[3][i][j] = mArray[3][i-1][j];
					}
				}
				mArray[4][0]++;
			} else {
				for (var i=0;i<fieldHeight;i++) {
					for (var j=0;j<fieldWidth;j++) {
						if (i >= fieldHeight-1) mArray[3][i][j] = false;
						else mArray[3][i][j] = mArray[3][i+1][j];
					}
				}
				mArray[4][0]--;
			}
		}
			
		if (go == 'turn') {
			var maxDim = Math.max(mArray[0][1][0],mArray[0][1][1]);
			var miniArray = setMatrixCells(null,maxDim,maxDim,false,true);

			var whileI = startI+maxDim;
			var whileJ = startJ+maxDim;
			
			for (var i=0;i<maxDim;++i) {
				for (var j=0;j<maxDim;++j) {
					if (startI+maxDim-j-1<fieldHeight)  miniArray[i][j] = mArray[3][startI+maxDim-j-1][startJ+i];
				}
			}
			if (!mArray[1]) {
				for (var i=0;i<maxDim;i++) {
					for (var j=0;j<maxDim;j++) {
						switchIt(miniArray[i][j],miniArray[maxDim-i-1][j]);
					}
				}
			}

			for (var i=startI;i<whileI;i++) {
				for (var j=startJ;j<whileJ;j++) {
					if (i<fieldHeight) mArray[3][i][j] = miniArray[i-startI][j-startJ];
				}
			}
		}
		
		if (go == 'up' || go == 'down') {
			if (go=='up') {
				for (var i=fieldWidth-1;i>=0;i--) {
					for (var j=0;j<fieldHeight;j++) {
						if (i <= 0) mArray[3][j][i] = false;
						else mArray[3][j][i] = mArray[3][j][i-1];
					}
				}
				mArray[4][1]++;
			} else {
				for (var i=0;i<fieldWidth;i++) {
					for (var j=0;j<fieldHeight;j++) {
						if (i >= fieldWidth-1) mArray[3][j][i] = false;
						else mArray[3][j][i] = mArray[3][j][i+1];
					}
				}
				mArray[4][1]--;
			}
		}
		
		if (tempCount != arrCheckSum(mArray[3]) || checkOverlap(mArray[3],mArray[1])) rollback(mArray,tempArray,startI,startJ);
		else if (go == 'turn'){		// Play "Turn"-Sound (only if actually turned!)
			playSndTurn();
		}
	}
	catch(err) {
		rollback(mArray,tempArray,startI,startJ)
	};
}

function rollback(arr,old,x,y) {
	arr[3] = copyField(old);
	arr[4][0] = x;
	arr[4][1] = y;
}

function arrCheckSum(arrCheck) {
	var c = 0;
	for (var i=0;i<fieldHeight;i++) {
		for (var j=0;j<fieldWidth;j++) {
			if (arrCheck[i][j]>0) c++;
		}
	}
	return c;
}

function checkOverlap(arr,ID) {
	for (var i=0;i<fieldHeight;i++) {
		for (var j=0;j<fieldWidth;j++) {
			if (arr[i][j] && (ID!=field[i][j])) return true;
		}
	}
	return false;
}

function copyField(inArray){
	var copyArray = setMatrixCells(null,fieldHeight,fieldWidth,false,true);
	for (var i=0;i<fieldHeight;i++) {
		for (var j=0;j<fieldWidth;j++) {
			copyArray[i][j] = inArray[i][j];
		}
	}
	return copyArray;
}

function arrIdentical(arr1,arr2) {
	for (var i=0;i<fieldHeight;i++) {
		for (var j=0;j<fieldWidth;j++) {
			if (arr1[i][j] != arr2[i][j]) return false;
		}
	}
	return true;
}

function fieldAdd(tempField,ID,ghost) {
	ghost = typeof ghost !== 'undefined' ? ghost : false;
	if (!ghost) {
	//if (ghost) var copyArray2 = copyField(field);
	if (ID) {
		for (var i=0;i<fieldHeight;i++) {
			var found = false;
			for (var j=0;j<fieldWidth;j++) {
				if (tempField[i][j]) {
					field[i][j] = !ID;
					//if (!ghost) field[i][j] = !ID;
					//else copyArray2[i][j] = !ID;
					found = true;
				}
			}
			if (found) checkRow(field,i,ID);
		}
	} else {
		for (var i=fieldHeight-1;i>=0;i--) {
			var found = false;
			for (var j=0;j<fieldWidth;j++) {
				if (tempField[i][j]) {
					field[i][j] = !ID;
					//if (!ghost) field[i][j] = !ID;
					//else copyArray2[i][j] = !ID;
					found = true;
				}
			}
			if (found) checkRow(field,i,ID);
		}
	}
	//if (ghost) if (!arrIdentical(copyArray2,field)) field = copyField(copyArray2);
	}
}

function checkRow(inArr,row,ID) {
	var c = 0;
	for (var j=0;j<fieldWidth;j++) {
		if (inArr[row][j] != ID) c++; 
	}
	if (c == fieldWidth) removeRow(inArr,row,ID);
}

function removeRow(inArr2,num,replacement) {
	for (var i=0;i<fieldWidth;i++) {
		inArr2[num][i]=replacement;
	}

	if (replacement) {
		for (var j=num;j>0;j--) {
			inArr2[j]=inArr2[j-1];
		}
	} else {
		for (var j=num;j<fieldHeight-1;j++) {
			inArr2[j]=inArr2[j+1];
		}
	}
	if (dropInterval>300) dropInterval *= 0.95; // Faster movement on line-full
	playSndLineFull();	  // Play "Line-Full"-Sound
}

function resetGame() {
	clearTimeout(running);
	removeCubes();
	addCubes();
	gameOver = 0;
	scene.remove(gameOverMesh1);
	scene.remove(gameOverMesh2);
	guiContainer.style.display = ''; 
}

function switchIt(a,b) {
	var newVal = a;
	a = b;
	b = newVal;
}